<?php
/**
 * @license http://www.gnu.org/licenses/lgpl.html LGPL
 * @author Harlan Iverson <h dot iverson at gmail dot com>
 * @copyright Copyright (c) 2006, Harlan Iverson.
 * @version $Id: PhpFaces.php 20 2006-08-01 05:54:40Z Harlan $
 */

define( "PhpFaces_InstallationDir", dirname(__FILE__)."/PhpFaces/" );

// it's 1000 times more likely that log4php.xml will be used
// so make that our default.
if( !defined("LOG4PHP_CONFIGURATION" ) ) {
	define( "LOG4PHP_CONFIGURATION", PhpFaces_InstallationDir."log4php.xml" );
}

require_once( "Pearified/Smarty/Smarty.class.php" );
require_once( "Pearified/Logging/Log4php/LoggerManager.php" );
require_once( "PhpFaces/EL/EL.php" );
require_once( "PhpFaces/Context/FacesContext.php" );
require_once( "PhpFaces/Config/FacesConfig.php" );
require_once( "PhpFaces/Lifecycle.php" );
require_once( "PhpFaces/Render/Render.php" );

/**
 * A class with one static method to conveniently run PhpFaces
 */
class PhpFaces {
	
	/**
	 * Do everything except run the Lifecycle; load config
	 * files and setup FacesContext / Application
	 */
	public static function SetupEnvironment( $configFile ) {
		$logger = LoggerManager::getLogger( "PhpFaces" );
		$logger->debug( "Setup Environment" );

		$config = new PhpFaces_Config_FacesConfig();
		
		
		// ========================================================
		//      Parse faces-config.xml files
		// ========================================================
		
		// make configFile always an array...
		if( is_array( $configFile ) ) {
			$configFileList = $configFile;
		} else {
			$configFileList = array( $configFile );
		}
		unset( $configFile );
		
		// put base-config.xml onto the beginning so it has
		// least precidence.
		// @todo directory calculation
		$baseConfig = PhpFaces_InstallationDir."base-config.xml";
		
		array_unshift( $configFileList, $baseConfig );
		
		foreach( $configFileList as $configFile ) {
			try {
				$config->parse( $configFile );
			} catch( Exception $e ) {
				
			}
		}

		foreach( $config->getRequiredFiles() as $file ) {
			require_once( $file );
		}
		
		
		// ========================================================
		//      Apply parsed config to application
		// ========================================================

		$lifecycleFactory = PhpFaces_LifecycleFactory::GetInstance();
		$lifecycle = $lifecycleFactory->getLifecycle( "default" );
		
		$lifecycle->setConfig( $config );
		

	
		// ************************** ATTN ****************************
		// all classes required to deserialize the session must
		// be included by this point!
		// ************************** ATTN ****************************
		if( !session_id() ) {
			session_start();
		} else {
			$logger->warn( "Session was already started... is this a problem?" );
		}
		
		
		$facesContext =& PhpFaces_Context_FacesContext::getCurrentInstance();
		$application = $facesContext->getApplication();

		//
		// Handle Navigation
		// create it with support for decoration
		// PhpFaces_Application_NavigationHandler
		//
		$navHandlerClasses = $config->getNavigationHandlers();
		
		// use the default
		if( $navHandlerClasses == null || count($navHandlerClasses) == 0 ) {
			$navHandlerClasses = array("PhpFaces_Application_NavigationHandlerImpl");
		}
		
		$navHandler = null;
		foreach( $navHandlerClasses as $class ) {
			$navHandler = new $class( $navHandler );
		}
		$application->setNavigationHandler( $navHandler );
		
		
		//
		// Handle View
		// PhpFaces_Application_ViewHandler
		//
		$viewHandlerClasses = $config->getViewHandlers();
		
		// use the default
		if( $viewHandlerClasses == null || count($viewHandlerClasses) == 0 ) {
			$viewHandlerClasses = array("PhpFaces_SmartyViewHandler");
		}
		
		$viewHandler = null;
		foreach( $viewHandlerClasses as $class ) {
			$viewHandler = new $class( $viewHandler );
		}
		$application->setViewHandler( $viewHandler );
		
		//
		// setup components
		//
		foreach( $config->getComponentEntries() as $componentEntry ) {
			$application->addComponent( $componentEntry->type, $componentEntry->class );	
		}
		

		//
		// setup render kit
		//
		
		$rkf = PhpFaces_Render_RenderKitFactory::getInstance();
		
		
		foreach( $config->getRenderKitEntries() as $renderKitEntry ) {
			$renderKitClassName = $renderKitEntry->class;
			$renderKit = new $renderKitClassName;
			
			foreach( $renderKitEntry->renderers as $rendererEntry ) {
				$rendererClass = $rendererEntry->class;
				$renderer = new $rendererClass;
				
				$renderKit->addRenderer( $rendererEntry->componentFamily, $rendererEntry->type, $renderer );
			}
			
			$rkf->addRenderKit( $renderKitEntry->id, $renderKit );
		}
		
		// FIXME I'm not sure where in the cycle
		// this should take place... 		
		foreach( $config->getManagedBeans() as $managedBean ) {
			$facesContext->registerBean( $managedBean->name, $managedBean->class, $managedBean->scope );
		}
		
		foreach( $config->getNavigationRules() as $navigationRule ) {
			$facesContext->registerNavigationRule( $navigationRule );
		}
		
		foreach( $config->getPhaseListeners() as $phaseListenerClass) {
			$phaseListener = new $phaseListenerClass;
			$lifecycle->addPhaseListener( $phaseListener );
		}

		
		
		return $facesContext;
	}
	
	
	/**
	 * Run PhpFaces given a config file. Does all stages of the request and
	 * all loading of dependencies, execution of actions, etc. Lots to talk about here.
	 * @param $configFile string the path to a faces-config.xml
	 */
	public static function RunFaces( $configFile = null ) {
	
		$logger = LoggerManager::GetLogger( "PhpFaces" );
		$logger->debug( "RunFaces" );
	
		$facesContext = null;
		if( $configFile != null ) {
			$facesContext = self::SetupEnvironment( $configFile );
		} else {
			$facesContext =& PhpFaces_Context_FacesContext::getCurrentInstance();
		}
		
		$lifecycleFactory = PhpFaces_LifecycleFactory::GetInstance();
		$lifecycle = $lifecycleFactory->getLifecycle( "default" );
		
		$lifecycle->execute( $facesContext );
		//
		// Handle View
		// PhpFaces_Application_ViewHandler
		//
		$lifecycle->render( $facesContext );
	
		if( $configFile != null ) {
			self::Cleanup();
		}
	}

	public static function Cleanup() {
		$logger = LoggerManager::GetLogger( "PhpFaces" );
		$logger->debug( "Cleanup" );
		LoggerManager::shutdown();
	}
}

/**
 * This class is used statically though the __autoload function. 
 * In the future it may be extended to support "3rd party" class
 * loaders to be registered with it.
 */
class PhpFaces_Classloader {
	/** Can not be instantiated */
	private function __construct(){}
	
	private static $logger;
	
	private static function getLogger() {
		if( self::$logger == null ) {
			self::$logger = LoggerManager::getLogger( "PhpFaces.Classloader" );
		}
		return self::$logger;
	}
	
	public static function loadClass( $className ) {
		
		$logger = self::getLogger();
		$logger->debug( "Attempting to load $className" );
		
		// static, so it only gets created once.
		static $classMap = array(
		
			// Classes
			"PhpFaces_Application_ResourceResolver" => "PhpFaces/ViewHandler/Smarty.php",
			"PhpFaces_ViewHandler_HardCore" => "PhpFaces/ViewHandler/Smarty.php",
			"PhpFaces_ViewHandler_Smarty" => "PhpFaces/ViewHandler/Smarty.php",
			"PhpFaces_ViewHandler_Phacelets" => "PhpFaces/ViewHandler/Smarty.php",
			"PhpFaces_Lang_ListObject" => "PhpFaces/ViewHandler/Smarty.php",
			"PhpFaces_Lang_MapObject" => "PhpFaces/ViewHandler/Smarty.php",
			"PhpFaces_Render_RenderKitFactory" => "PhpFaces/Render/RenderKit.php",
			"PhpFaces_Render_RenderKit_HTML_BASIC" => "PhpFaces/Render/RenderKit.php",
			"PhpFaces_XHTML" => "PhpFaces/Render/RenderKit.php",
			"PhpFaces_RenderKit_Html_UIOutputRenderer" => "PhpFaces/Render/RenderKit.php",
			"PhpFaces_RenderKit_Html_UIInputRenderer" => "PhpFaces/Render/RenderKit.php",
			"PhpFaces_RenderKit_Html_UIViewRootRenderer" => "PhpFaces/Render/RenderKit.php",
			"PhpFaces_Component_UIViewRoot" => "PhpFaces/Component/UIComponent.php",
			"PhpFaces_EL_ValueBinding" => "PhpFaces/EL/EL.php",
			"PhpFaces_Component_UIOutput" => "PhpFaces/Component/UIComponent.php",
			"PhpFaces_Component_UIInput" => "PhpFaces/Component/UIComponent.php",
			"PhpFaces_EL_VariableNotFoundException" => "PhpFaces/FacesContext.class.php",
			"PhpFaces_Application_Application" => "PhpFaces/FacesContext.class.php",
			"PhpFaces_Context_FacesContext" => "PhpFaces/FacesContext.class.php",
			"PhpFaces_Config_NavigationRuleConfigEntry" => "PhpFaces/FacesConfig.php",
			"PhpFaces_Config_ManagedBeanConfigEntry" => "PhpFaces/FacesConfig.php",
			"PhpFaces_Config_ComponentEntry" => "PhpFaces/FacesConfig.php",
			"PhpFaces_Config_XMLReader" => "PhpFaces/FacesConfig.php",
			"PhpFaces_Config_FacesConfig" => "PhpFaces/FacesConfig.php",
			"PhpFaces_Taglib_Tag" => "PhpFaces/FacesConfig.php",
			"PhpFaces_Taglib" => "PhpFaces/FacesConfig.php",
			"PhpFaces_ViewHandler_FaceletViewHandler" => "PhpFaces/FacesConfig.php",
			"PhpFaces_LifecycleFactory" => "PhpFaces/Lifecycle.php",
			"PhpFaces_PhaseId" => "PhpFaces/Lifecycle.php",
			"PhpFaces_Event_PhaseEvent" => "PhpFaces/Lifecycle.php",
			"PhpFaces_Event_PhaseListenerManager" => "PhpFaces/Lifecycle.php",
			"PhpFaces_LifecycleImpl" => "PhpFaces/Lifecycle.php",
			"PhpFaces_FaceletsTemplateResource" => "PhpFaces/ViewHandler.class.php",
			"PhpFaces_Smarty_Core" => "PhpFaces/ViewHandler.class.php",
			"PhpFaces_UIInputStruct" => "PhpFaces/ViewHandler.class.php",
			"PhpFaces_Smarty_HtmlWidgets" => "PhpFaces/ViewHandler.class.php",
			"HtmlColumn" => "PhpFaces/ViewHandler.class.php",
			"UIData" => "PhpFaces/ViewHandler.class.php",
			"HtmlDataTable" => "PhpFaces/ViewHandler.class.php",
			"ListDataModel" => "PhpFaces/ViewHandler.class.php",
			"PhpFaces" => "PhpFaces/PhpFaces.php",
			"PhpFaces_Classloader" => "PhpFaces/PhpFaces.php",
			"PhpFaces_Makeshift_UIViewRoot" => "PhpFaces/PhpFaces.php",
			"PhpFaces_SmartyViewHandler" => "PhpFaces/PhpFaces.php",
			"PhpFaces_Application_NavigationHandlerImpl" => "PhpFaces/PhpFaces.php",
			
			
			// Interfaces
			"PhpFaces_Component_NamingContainer" =>  "PhpFaces/Component/UIComponent.php",
			"PhpFaces_Component_ValueHolder" =>  "PhpFaces/Component/UIComponent.php",
			"PhpFaces_Event_FacesListener" => "PhpFaces/Lifecycle.php",
			"PhpFaces_Event_PhaseListener" => "PhpFaces/Lifecycle.php",
			"PhpFaces_Lifecycle" => "PhpFaces/Lifecycle.php"
		);	


		if( array_key_exists( $className, $classMap ) ) {
			$file = $classMap[ $className ];
			
			$logger->debug( "$className resolved to file '$file'" );

			// no need to keep a table of included files, 
			// PHP has one build in :D
			require_once( $file );
		}
	}
}
/*
function __autoload( $className ) {
		PhpFaces_Classloader::loadClass( $className );
}
*/

class PhpFaces_Makeshift_UIViewRoot {
	public $viewId;
}

/**
 * The default view handler for PhpFaces; runs Facelets parsing on top of Smarty.
 */
class PhpFaces_SmartyViewHandler extends PhpFaces_Application_ViewHandler {
	
	private $logger;
	
	public function __construct() {
		$this->logger = LoggerManager::GetLogger( "PhpFaces.SmartyViewHandler" );
	}
	
	/**
	 * Runs the View Handler on a given context
	 * @todo this could be abstracted a bit more
	 */
	public function createView( PhpFaces_Context_FacesContext $facesContext, $viewId ) {
		$view = new PhpFaces_Component_UIViewRoot();
		$view->setViewId( $viewId );
		
		$this->logger->debug( "createView: $viewId" );
		
		return $view;
	}
	public function renderView( PhpFaces_Context_FacesContext $facesContext, PhpFaces_Component_UIViewRoot $viewToRender ) {

		$smarty = new Smarty();
		call_user_func( array("PhpFaces_FaceletsTemplateResource", "register"),  $smarty );	
		
		$page = $viewToRender->getViewId();
		
		$this->logger->debug( "renderView: $page" );
		

		if( file_exists( $smarty->template_dir."/pages/$page.jsf") ) {
			$output = $smarty->fetch( "facelets://pages/$page.jsf" );
		} else {
			header( "Status: 404" );
			$output = "Page Not Found.";
		}

		$this->logger->debug( "Sending contents" );
		echo $output;
	}
	
}

?>
